
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "utility.h"
#include "qr-common.h"

static void qr_put_finder_pattern(unsigned char* frame, int width);
static void qr_put_finder_pattern_internal(unsigned char* frame, int width, int x, int y);
static void qr_put_separator(unsigned char* frame, int width);
static void qr_put_mask_format_information(unsigned char* frame, int width);
static void qr_put_timing_pattern(unsigned char* frame, int width);
static void qr_put_alignment_pattern(int version, unsigned char* frame);
static void qr_put_alignment_pattern_internal(unsigned char* frame, int width, int center_x, int center_y);
static void qr_put_version_information_pattern(int version, unsigned char* frame);

unsigned char* qr_new_frame(int version)
{
	int width, size;
	unsigned char* frame;

	if(qr_check_version(version) < 0)
	{
		RES_NULL("Argument is invalid.");
	}

	width = qr_width(version);
	if(width <= 0)
	{
		RES_NULL("Invoke qr_width error.");
	}
	size = width * width * sizeof(unsigned char);
	frame = (unsigned char*)malloc(size);
	if(NULL == frame)
	{
		RES_NULL("Invoke malloc error.");
	}
	memset(frame, 0, size);
	qr_put_finder_pattern(frame, width);
	qr_put_separator(frame, width);
	qr_put_mask_format_information(frame, width);
	qr_put_timing_pattern(frame, width);
	qr_put_alignment_pattern(version, frame);
	qr_put_version_information_pattern(version, frame);
	// A little bit.
	frame[width * (width - 8) + 8] = 0x81;
	return frame;
}

int qr_put_format_information(int version, ECLevel ec_level, MaskPattern mask_pattern, unsigned char* frame)
{
	int black_count, width, i;
	unsigned int format;
	unsigned char value;

	if(qr_check_version(version) < 0 || qr_check_ec_level(ec_level) < 0 || qr_check_mask_pattern(mask_pattern < 0) || NULL == frame)
	{
		RES_INVAL("Argument is invalid.");
	}

	black_count = 0;
	width = qr_width(version);
	if(width <= 0)
	{
		RES_INVAL("Invoke qr_width error.");
	}
	format = qr_format_information(ec_level, mask_pattern);
	for(i = 0; i < 8; ++i)
	{
		if(0 == (format & 1))
		{
			value = 0x84;
		}
		else
		{
			value = 0x85;
			black_count += 2;
		}
		frame[(width << 3) + width - 1 - i] = value;
		if(i < 6)
		{
			frame[width * i + 8] = value;
		}
		else
		{
			frame[width * (i + 1) + 8] = value;
		}
		format = format >> 1;
	}
	for(i = 0; i < 7; ++i)
	{
		if(0 == (format & 1))
		{
			value = 0x84;
		}
		else
		{
			value = 0x85;
			black_count += 2;
		}
		frame[width * (width - 7 + i) + 8] = value;
		if(i != 0)
		{
			frame[(width << 3) + 6 - i] = value;
		}
		else
		{
			frame[(width << 3) + 7] = value;
		}
		format = format >> 1;
	}
	return black_count;
}


void qr_put_finder_pattern(unsigned char* frame, int width)
{
	assert(frame != NULL && width > 0);
	
	qr_put_finder_pattern_internal(frame, width, 0, 0);
	qr_put_finder_pattern_internal(frame, width, width - 7, 0);
	qr_put_finder_pattern_internal(frame, width, 0, width - 7);
}

void qr_put_finder_pattern_internal(unsigned char* frame, int width, int x, int y)
{
	const unsigned char* ptr;
	int i, j;

	assert(frame != NULL && width > 0 && x >= 0 && y >= 0 && x + 6 < width && y + 6 < width);

	ptr = finderPattern;
	frame += y * width + x;
	for(i = 0; i < 7; ++i)
	{
		for(j = 0; j < 7; ++j)
		{
			frame[j] = ptr[j];
		}
		frame += width;
		ptr += 7;
	}
}

void qr_put_separator(unsigned char* frame, int width)
{
	unsigned char* ptr1, * ptr2;
	int i;

	assert(frame != NULL && width > 0);

	ptr1 = frame;
	ptr2 = frame + width * (width - 7);
	for(i = 0; i < 7; ++i)
	{
		ptr1[7] = 0xc0;
		ptr1[width - 8] = 0xc0;
		ptr2[7] = 0xc0;
		ptr1 += width;
		ptr2 += width;
	}
	memset(frame + width * 7, 0xc0, 8);
	memset(frame + width * 8 - 8, 0xc0, 8);
	memset(frame + width * (width - 8), 0xc0, 8);
}

void qr_put_mask_format_information(unsigned char* frame, int width)
{
	unsigned char* ptr;
	int i;

	assert(frame != NULL && width > 0);

	memset(frame + width * 8, 0x84, 9);
	memset(frame + width * 9 - 8, 0x84, 8);
	ptr = frame + 8;
	for(i = 0; i < 8; ++i)
	{
		*ptr = 0x84;
		ptr += width;
	}
	ptr = frame + width * (width - 7) + 8;
	for(i = 0; i < 7; ++i)
	{
		*ptr = 0x84;
		ptr += width;
	}
}

void qr_put_timing_pattern(unsigned char* frame, int width)
{
	unsigned char* ptr1, * ptr2;
	int i;
	unsigned char value;

	assert(frame != NULL && width > 0);

	ptr1 = frame + width * 6 + 8;
	ptr2 = frame + width * 8 + 6;
	for(i = 1; i < width - 15; ++i)
	{
		value = ((0 == (i & 1)) ? 0x90 : 0x91);
		*ptr1 = value;
		*ptr2 = value;
		++ptr1;
		ptr2 += width;
	}
}

void qr_put_alignment_pattern(int version, unsigned char* frame)
{
	int width, pos0, distance, count;
	int center_x, center_y, i, j;

	assert(0 == qr_check_version(version) && frame != NULL);

	if(version < 2)
	{
		assert(1 == version);
		return;
	}
	width = qr_width(version);
	assert(width > 0);
	pos0 = alignmentPatternPosition[version][0];
	assert(pos0 + 2 < width);
	distance = alignmentPatternPosition[version][1] - pos0;
	assert(distance != 0);
	if(distance < 0)
	{
		qr_put_alignment_pattern_internal(frame, width, pos0, pos0);
	}
	else	// distance > 0
	{
		count = (width - pos0 - 2) / distance + 1;
		center_x = pos0;
		for(i = 0; i < count - 1; ++i)
		{
			qr_put_alignment_pattern_internal(frame, width, 6, center_x);
			qr_put_alignment_pattern_internal(frame, width, center_x, 6);
			center_x += distance;
		}
		center_y = pos0;
		for(i = 0; i < count; ++i)
		{
			center_x = pos0;
			for(j = 0; j < count; ++j)
			{
				qr_put_alignment_pattern_internal(frame, width, center_x, center_y);
				center_x += distance;
			}
			center_y += distance;
		}
	}
}

void qr_put_alignment_pattern_internal(unsigned char* frame, int width, int center_x, int center_y)
{
	const unsigned char* ptr;
	int i, j;

	assert(frame != NULL && width > 0 && center_x >= 2 && center_y >= 2 && center_x + 2 < width && center_y + 2 < width);

	ptr = alignmentPattern;
	frame += (center_y - 2) * width + center_x - 2;
	for(i = 0; i < 5; ++i)
	{
		for(j = 0; j < 5; ++j)
		{
			frame[j] = ptr[j];
		}
		frame += width;
		ptr += 5;
	}
}

void qr_put_version_information_pattern(int version, unsigned char* frame)
{
	int width;
	unsigned int info, value;
	unsigned char* ptr;
	int i, j;

	assert(0 == qr_check_version(version) && frame != NULL);

	if(version < 7)
	{
		return;
	}
	width = qr_width(version);
	assert(width > 0);
	info = versionInformationPattern[version];
	ptr = frame + width * (width - 11);
	value = info;
	for(i = 0; i < 6; ++i)
	{
		for(j = 0; j < 3; ++j)
		{
			ptr[width * j + i] = ((0 == (value & 1)) ? 0x88 : 0x89);
			value = value >> 1;
		}
	}
	ptr = frame + width - 11;
	value = info;
	for(i = 0; i < 6; ++i)
	{
		for(j = 0; j < 3; ++j)
		{
			ptr[j] = ((0 == (value & 1)) ? 0x88 : 0x89);
			value = value >> 1;
		}
		ptr += width;
	}
}

